/*
 *          Copyright Andrey Semashev 2007 - 2013.
 * Distributed under the Boost Software License, Version 1.0.
 *    (See accompanying file LICENSE_1_0.txt or copy at
 *          http://www.boost.org/LICENSE_1_0.txt)
 */
/*!
 * \file   locking_ptr.hpp
 * \author Andrey Semashev
 * \date   15.07.2009
 *
 * This header is the Boost.Log library implementation, see the library documentation
 * at http://www.boost.org/libs/log/doc/log.html.
 */

#ifndef BOOST_LOG_DETAIL_LOCKING_PTR_HPP_INCLUDED_
#define BOOST_LOG_DETAIL_LOCKING_PTR_HPP_INCLUDED_

#include <boost/shared_ptr.hpp>
#include <boost/log/detail/config.hpp>
#include <boost/log/utility/explicit_operator_bool.hpp>
#include <boost/log/detail/header.hpp>

#ifdef BOOST_LOG_HAS_PRAGMA_ONCE
#pragma once
#endif

namespace boost {

BOOST_LOG_OPEN_NAMESPACE

namespace aux {

    //! Shared lock object to support locking_ptr
    struct BOOST_LOG_NO_VTABLE locking_ptr_counter_base
    {
        unsigned int m_RefCounter;

        locking_ptr_counter_base() : m_RefCounter(0)
        {
        }

        virtual ~locking_ptr_counter_base() {}
        virtual void lock() = 0;
        virtual bool try_lock() = 0;
        virtual void unlock() = 0;

    private:
        locking_ptr_counter_base(locking_ptr_counter_base const&);
        locking_ptr_counter_base& operator= (locking_ptr_counter_base const&);
    };

    struct try_lock_tag {};
    const try_lock_tag try_lock = {};

    //! A pointer type that locks the backend until it's destroyed
    template< typename T >
    class locking_ptr
    {
    public:
        //! Pointed type
        typedef T element_type;

    private:
        //! The pointer to the backend
        shared_ptr< element_type > m_pElement;
        //! Reference to the shared lock control object
        locking_ptr_counter_base* m_pLock;

    public:
        //! Constructor
        locking_ptr(shared_ptr< element_type > const& p, locking_ptr_counter_base& l)
            : m_pElement(p), m_pLock(&l)
        {
            if (m_pLock->m_RefCounter == 0)
                m_pLock->lock();
            ++m_pLock->m_RefCounter;
        }
        //! Constructor
        locking_ptr(shared_ptr< element_type > const& p, locking_ptr_counter_base& l, try_lock_tag const&)
            : m_pElement(p), m_pLock(&l)
        {
            if (m_pLock->m_RefCounter > 0 || m_pLock->try_lock())
            {
                ++m_pLock->m_RefCounter;
            }
            else
            {
                m_pElement.reset();
                m_pLock = NULL;
            }
        }
        //! Copy constructor
        locking_ptr(locking_ptr const& that) : m_pElement(that.m_pElement), m_pLock(that.m_pLock)
        {
            if (m_pLock)
                ++m_pLock->m_RefCounter;
        }
        //! Destructor
        ~locking_ptr()
        {
            if (m_pLock && --m_pLock->m_RefCounter == 0)
                m_pLock->unlock();
        }

        //! Assignment
        locking_ptr& operator= (locking_ptr that)
        {
            this->swap(that);
            return *this;
        }

        //! Indirection
        element_type* operator-> () const { return m_pElement.get(); }
        //! Dereferencing
        element_type& operator* () const { return *m_pElement; }

        //! Accessor to the raw pointer
        element_type* get() const { return m_pElement.get(); }

        //! Checks for null pointer
        BOOST_LOG_EXPLICIT_OPERATOR_BOOL()
        //! Checks for null pointer
        bool operator! () const { return !m_pElement; }

        //! Swaps two pointers
        void swap(locking_ptr& that)
        {
            m_pElement.swap(that.m_pElement);
            register locking_ptr_counter_base* p = m_pLock;
            m_pLock = that.m_pLock;
            that.m_pLock = p;
        }
    };

    //! Free raw pointer getter to assist generic programming
    template< typename T >
    inline T* get_pointer(locking_ptr< T > const& p)
    {
        return p.get();
    }
    //! Free swap operation
    template< typename T >
    inline void swap(locking_ptr< T >& left, locking_ptr< T >& right)
    {
        left.swap(right);
    }

} // namespace aux

BOOST_LOG_CLOSE_NAMESPACE // namespace log

} // namespace boost

#include <boost/log/detail/footer.hpp>

#endif // BOOST_LOG_DETAIL_LOCKING_PTR_HPP_INCLUDED_
